home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d12 / vol6n16.arc / RN.ASM < prev    next >
Assembly Source File  |  1987-08-29  |  83KB  |  1,784 lines

  1. ;                     RN.ASM
  2. ;    RN [d:][/I]  /I option installs resident database
  3.  
  4. CODE SEGMENT                           ;*************************
  5. ASSUME CS:CODE,DS:CODE                 ;*                       *
  6. ORG 100H                               ;*  REMEMBER TO EXE2BIN  *
  7.                                        ;*                       *
  8. START:         JMP    BEGINNING        ;*************************
  9.  
  10. ;              DATA AREA
  11. ;              ---------
  12. INSTALL_MSG    DB     'RN sucessfully installed',13,10
  13. COPYRIGHT      DB     'Copyright 1987 Ziff-Davis Publishing Co.',13,10
  14. PROGRAMMER     DB     'Michael J. Mefford$',1AH
  15. NO_FREE        DB     'Too many resident programs$'
  16. REQUIRES       DB     'Requires DOS 2.x$'
  17. NOT_ENOUGH     DB     'Requires 128K free RAM$'
  18. UNKNOWN        DB     'Drive not supported$'
  19. HEADING        DB     'Directories of drive ',0
  20. DIR_MSG        DB     ' directories',0
  21. FILE_COUNT     DB     ' files in ',0
  22. USING          DB     ' using ',0
  23. LOADING        DB     'Loading and sorting directories',13,10,0
  24. RENAME_MSG     DB     'Enter new name for ',0
  25. PATH_LENGTH    DB     'Path length is now ',0
  26.                DB     '.  Maximum is 63.',0
  27. MKDIR_MSG      DB     'Enter new subdirectory for ',0
  28. RMDIR_MSG      DB     'Warning: All files will be lost.',0
  29.                DB     'Do you still wish to remove it?  Y/N ',0
  30. HIDE_MSG       DB     'H to hide or U to unhide ',0
  31. READ_ONLY_MSG  DB     'R to mark read-only U to undo read-only',0
  32. ARCHIVE_MSG    DB     'S to set R to reset archive bit',0
  33. HAVE_BEEN      DB     'have been marked ',0
  34.                DB     'not to be BACKUPed.',0
  35. READ_MSG       DB     'not read-only.',0
  36. BS             DB     8,32,8,0
  37.  
  38. DATA_INT       DB     0
  39. DATA_SEGMENT   DW     ?
  40.  
  41. DOS_VERSION    DB     ?
  42. ROOT           DB     '\ (ROOT)'
  43. ROOT_DIR       DB     '\',0
  44. DOT_DOT        DB     '..',0
  45. STAR_DOT_STAR  DB     '*.*',0
  46. DEFAULT_DRIVE  DB     ?
  47. DRIVE          DB     ?
  48. ROOT_SECTORS   DW     ?
  49. CLUST_RECORDS  DW     ?
  50. CLUST_SECTORS  DW     ?
  51. CLUST_BYTES    DW     ?
  52. CLUST_COUNT    DW     ?
  53. LAST_CLUSTER   DW     ?
  54. LAST_ENTRY     DW     ?
  55. END_OFFSET     DW     ?
  56. STRIP_MASK     DB     ?
  57. ADD_MASK       DB     ?
  58.  
  59. STATUS_REG     DW     ?
  60. VIDEO_SEG      DW     0B000H
  61. NORMAL         DB     07H
  62. INVERSE        DB     70H
  63. BAR_ATTRIBUTE  DB     70H
  64. CURSOR_TYPE    DW     ?
  65. CUR_OFFSET     DW     DIRECTORIES
  66. CUR_RECORD     DW     ?
  67. LINE           DW     323
  68. PAGE_END       DW     21*160+323
  69. DIR_COUNT      DW     1
  70. COUNT          DW     ?
  71. BAR_START      DW     323
  72. BAR_LENGTH     DW     8
  73. SEARCH_COUNT   DW     ?
  74. DIR_NAME       DW     ?
  75.  
  76. COMSPEC        DB     'COMSPEC='
  77. PARAMETER      DB     7,'/C DR/O',13
  78.  
  79. ENVIRONMENT    DW     ?
  80. COM_LINE_PTR   DD     OFFSET PARAMETER
  81. STACK_SEG      DW     ?
  82. STACK_PTR      DW     ?
  83.  
  84. DIR_RECORD     EQU    40
  85. DATA_RECORD    EQU    20
  86.  
  87. ROOT_DIRECTORY EQU    0
  88. FAT            EQU    ROOT_DIRECTORY
  89. WORKSPACE      EQU    65535-16384
  90.  
  91. DISPATCH_KEY   DB  1,1CH,3BH,3CH,3DH,3EH,3FH,40H,41H,42H,43H
  92.                DB  44H,47H,48H,49H,4FH,50H,51H,76H,84H
  93.  
  94. DISPATCH_TABLE DW  ESC_EXIT, CHANGE_DIR,  CHANGE_DIR, RENAME,     MKDIR
  95.                DW  RMDIR,    HIDE_UNHIDE, READ_ONLY,  SET_RESET,  REREAD
  96.                DW  FILE_SUM, DR,          HOME_BAR,   UP_ARROW,   PG_UP
  97.                DW  END_BAR,  DN_ARROW,    PG_DN,      BOTTOM_BAR, TOP_BAR
  98.  
  99. MENU  LABEL  BYTE
  100. DB  201,20 DUP (205),187,186,' PC Magazine RN.COM ',186
  101. DB  186,' (C) Copr. 1987 ZD  ',186,186,' Michael J. Mefford ',186
  102. DB  199,20 DUP (196),182,186,' F1   ChDir (or ',17,196,217,')',186
  103. DB  186,' F2   Rename        ',186,186,' F3   MkDir         ',186
  104. DB  186,' F4   RmDir         ',186,186,' F5   Hide/Unhide   ',186
  105. DB  186,' F6   Do/Undo R-O   ',186,186,' F7   Set/Reset Arc ',186
  106. DB  186,' F8   Reread        ',186,186,' F9   File count    ',186
  107. DB  186,' F10  DR            ',186,186,' Esc  to Exit       ',186
  108. DB  200,20 DUP (205),188,'  Use: ',24,32,25,' PgUp PgDn  '
  109. DB  ' ^PgUp ^PgDn Home End '
  110.  
  111. ;---------------------------------------------------------------------;
  112. ; Store our data segment.  We will be writing directly to the screen  ;
  113. ; buffer so we need the display card address and the status register. ;
  114. ;---------------------------------------------------------------------;
  115.  
  116. ;              CODE AREA
  117. ;              ---------
  118. BEGINNING:     CLD                     ;String moves in forward direction.
  119.                PUSH   DS
  120.                POP    DATA_SEGMENT     ;Store our data segment.
  121.                MOV    AX,3301H         ;Turn Control Break off.
  122.                MOV    DL,0
  123.                INT    21H
  124.  
  125.                MOV    AX,40H           ;Point to the ROM BIOS data area
  126.                MOV    DS,AX            ; and get base address of active
  127.                MOV    AX,DS:[63H]      ; display card.
  128.                ADD    AX,6             ;Add six to get status register
  129.                PUSH   CS               ;Done there, so restore data segment.
  130.                POP    DS
  131.                MOV    STATUS_REG,AX    ;Store status register.
  132.                CMP    AX,3BAH          ;Status port of MONO card is 3BAh.
  133.                JZ     MONO             ;If that's what we got, it's MONO
  134.                MOV    VIDEO_SEG,0B800H       ; else COLOR so add 800h.
  135.                XOR    BH,BH                  ;Get current attribute
  136.                MOV    AH,8                   ; of display page zero.
  137.                INT    10H
  138.                MOV    NORMAL,AH              ;Store it.
  139.                XOR    AH,1110111B            ;Flip color bits.
  140.                MOV    INVERSE,AH             ;Save it.
  141.                MOV    BAR_ATTRIBUTE,AH
  142. MONO:          XOR    BH,BH
  143.                MOV    AH,3                   ;Retrieve cursor type.
  144.                INT    10H
  145.                MOV    CURSOR_TYPE,CX         ;And save it.
  146.                CALL   CURSOR_OFF
  147.  
  148. ;---------------------------------------------;
  149. ; Check DOS version and allocate 128K memory. ;
  150. ;---------------------------------------------;
  151.  
  152.                MOV    AH,30H                 ;Get DOS version.
  153.                INT    21H
  154.                MOV    DOS_VERSION,AL         ;And save.
  155.                CMP    AL,2                   ;Is it 2.x or above?
  156.                JAE    CONTINUE               ;If yes, continue.
  157.  
  158.                MOV    DX,OFFSET REQUIRES     ;Else, exit with message.
  159. QUIT:          JMP    MSG_EXIT
  160.  
  161. CONTINUE:      CMP    AL,3                          ;Is it DOS 3.x or above?
  162.                JAE    RENAME_OK                     ;If yes continue.
  163.                MOV    BYTE PTR MENU+(22*6)+14,'N'   ;Else mark rename function
  164.                MOV    BYTE PTR MENU+(22*6)+15,'/'   ; Not/Available.
  165.                MOV    BYTE PTR MENU+(22*6)+16,'A'
  166. RENAME_OK:     MOV    AH,49H                        ;Return our memory segment.
  167.                INT    21H
  168.                MOV    BX,2000H               ;Request 128K.
  169.                MOV    AH,4AH
  170.                INT    21H
  171.                MOV    DX,OFFSET NOT_ENOUGH   ;If not available, exit
  172.                JC     QUIT                   ; with message.
  173.  
  174.                MOV    AH,19H                 ;Get default drive and save.
  175.                INT    21H
  176.                MOV    DEFAULT_DRIVE,AL
  177.  
  178. ;-----------------------------------------------------;
  179. ; Check for drive request.  Retrieve Disk Block info. ;
  180. ;-----------------------------------------------------;
  181.  
  182.                MOV    AL,DS:[5CH]            ;Get drive request.
  183.                MOV    BL,AL                  ;Save it.
  184.                DEC    AL                     ;Adjust.
  185.                OR     BL,BL                  ;If it was zero, no request.
  186.                JNZ    STORE_DRIVE
  187.  
  188.                MOV    AH,19H                 ;Then get default drive.
  189.                INT    21H
  190. STORE_DRIVE:   MOV    DRIVE,AL               ;Store it.
  191.                MOV    DL,AL
  192.                MOV    AH,0EH                 ;Change drive to requested.
  193.                INT    21H
  194.  
  195.                MOV    DL,DRIVE               ;Retrieve drive.
  196.                INC    DL                     ;Adjust.
  197.                MOV    AH,32H                 ;Retrieve Disk Block.
  198.                INT    21H
  199.                OR     AL,AL                  ;Does disk request exist?
  200.                JZ     EXISTS
  201.                JMP    DISK_EXIT              ;If no, exit with message.
  202. EXISTS:        MOV    SI,BX                  ;Store Disk Block.
  203.                MOV    DI,OFFSET DISK_BLOCK
  204.                MOV    CX,18
  205.                REP    MOVSB
  206.                PUSH   CS                     ;Restore data segment.
  207.                POP    DS
  208.  
  209.                CMP    DRIVE,1                ;Is drive request A: or B:?
  210.                JA     CK_FREE
  211.                JMP    READ                   ;If yes, automatic read.
  212.  
  213. ;---------------------------------------------------;
  214. ; Check to see if our database has been installed.  ;
  215. ;---------------------------------------------------;
  216.  
  217. CK_FREE:       MOV    AL,60H - 1             ;Available vectors are 60H - 67H.
  218. FREE_USER_INT: INC    AL
  219.                MOV    AH,35H                 ;Get vector address.
  220.                INT    21H
  221.                CMP    BX,0                   ;Is offset being used?
  222.                JNZ    CK_SIGNATURE           ;If yes, see if it's us.
  223.                MOV    DX,ES
  224.                CMP    DX,0                   ;Is segment being used?
  225.                JNZ    CK_SIGNATURE           ;If yes, see if it's us.
  226.                MOV    DATA_INT,AL            ;If available, save INT number.
  227.                JMP    SHORT NEXT_USER        ;Check all 7.
  228.  
  229. CK_SIGNATURE:  MOV    DI,BX                  ;See if INT has our signature.
  230.                MOV    SI,OFFSET INSTALL_MSG
  231.                MOV    CX,30/2
  232.                REPZ   CMPSW
  233.                JZ     CK_SUBST               ;If yes, already installed.
  234.  
  235. NEXT_USER:     CMP    AL,67H                 ;Have we checked all 7?
  236.                JNZ    FREE_USER_INT          ;If no, next one.
  237.                CMP    DATA_INT,0             ;Did we find one that was free?
  238.                JNZ    CK_SWITCH
  239.                MOV    DX,OFFSET NO_FREE      ;If no, exit with message.
  240.                JMP    ERROR_EXIT
  241.  
  242. CK_SWITCH:     MOV    SI,80H                 ;See if request to install.
  243.                LODSB
  244.                CMP    AL,0                   ;If no parameters, read data.
  245.                JZ     READ
  246. NEXT_SWITCH:   LODSB
  247.                CMP    AL,13                  ;End of parameters?
  248.                JZ     READ                   ;If yes, read data.
  249.                CMP    AL,'/'                 ;Switch character?
  250.                JNZ    NEXT_SWITCH
  251.                LODSB
  252.                AND    AL,5FH
  253.                CMP    AL,'I'                 ;If yes, is it Install switch?
  254.                JNZ    READ
  255.  
  256. INSTALL:       MOV    DX,103H                ;If yes, point to signature.
  257.                MOV    AL,DATA_INT            ;Retrieve free INT number.
  258.                MOV    AH,25H                 ;And install.
  259.                INT    21H
  260.                CALL   FIRST_READ             ;Read disk data.
  261.                CALL   CLS
  262.                MOV    DX,OFFSET INSTALL_MSG  ;Display install message.
  263.                MOV    AH,9
  264.                INT    21H
  265.                CALL   RESTORE_DRIVE          ;Restore defaults.
  266.                CALL   CURSOR_ON
  267.                MOV    DX,OFFSET END_RESIDENT
  268.                INT    27H                    ;Terminate and stay resident.
  269.  
  270. ;-----------------------------------------------------;
  271. ; Check to see if drive request is a substitute.      ;
  272. ; Also if different from current resident drive data. ;
  273. ;-----------------------------------------------------;
  274.  
  275. CK_SUBST:      MOV    DATA_SEGMENT,ES        ;Save the location of disk data.
  276.                PUSH   ES:CLUST_BYTES         ;Retrieve from resident database
  277.                POP    CLUST_BYTES            ; cluster size in bytes.
  278.                MOV    AL,DRIVE
  279.                MOV    DX,OFFSET UNKNOWN      ;Is it a substituted drive?
  280.                CMP    AL,BYTE PTR DISK_BLOCK
  281.                JZ     CK_NEW_DRIVE           ;If yes, exit with message.
  282.                JMP    DISK_EXIT
  283.  
  284. CK_NEW_DRIVE:  CMP    AL,ES:DRIVE            ;Is it a new drive request?
  285.                JZ     FORMAT                 ;If no, format resident data.
  286.                MOV    ES:DRIVE,AL            ;Else, save new drive.
  287.  
  288. ;--------------------------------------------------------------------;
  289. ; Read disk directory data, if necessary.  Format it into a tree.    ;
  290. ; Mark the current default directory.  Clear the screen and display. ;
  291. ;--------------------------------------------------------------------;
  292.  
  293. READ:          CALL   FIRST_READ
  294. FORMAT:        CALL   FORMAT_IT
  295.                CALL   GET_CURRENT
  296.                CALL   CLS
  297.                CALL   START_DISPLAY
  298.  
  299. ;-----------------------------------------;
  300. ; We are now ready for business.  We will ;
  301. ; loop here, waiting for user keystokes.  ;
  302. ;-----------------------------------------;
  303.  
  304. GET_KEY:       CALL   UPDATE_SCREEN
  305.                CALL   READ_KEY               ;Get a keystroke.
  306.                MOV    BX,AX                  ;Save returned key.
  307.                CMP    AH,1                   ;Is it Esc or below?
  308.                JBE    FUNCTION               ;If yes, function.
  309.                CMP    AH,36H                 ;Is it right shift or above?
  310.                JAE    FUNCTION               ;If yes, function.
  311.                CMP    AH,1CH                 ;Is it CR?
  312.                JZ     FUNCTION               ;If yes, function.
  313.                CALL   SEARCH                 ;Else, search request.
  314.                JMP    SHORT GET_KEY
  315. FUNCTION:      MOV    DI,OFFSET DISPATCH_KEY
  316.                MOV    AL,BH
  317.                MOV    CX,20                  ;20 valid commands.
  318.                REPNZ  SCASB
  319.                JNZ    GET_KEY                       ;If no match, get another.
  320.                MOV    DI,OFFSET DISPATCH_TABLE+38
  321.                SHL    CX,1
  322.                SUB    DI,CX
  323.                CALL   DS:[DI]                ;Else, do subroutine.
  324.                JMP    SHORT GET_KEY          ;Update screen; get next command.
  325.  
  326. ;-------------------------------------------------------------------;
  327. ; This is the exit routine. Restore defaults the way we found them. ;
  328. ;-------------------------------------------------------------------;
  329.  
  330. ESC_EXIT:      MOV    DX,OFFSET CURRENT_DIR
  331.                CALL   CD
  332. EXIT:          CALL   RESTORE_DRIVE
  333.                CALL   CLS
  334.                CALL   CURSOR_ON
  335.                XOR    DX,DX                  ;Home cursor.
  336.                CALL   SET_CURSOR
  337.                INT    20H                    ;And terminate.
  338.  
  339.                ;*************;
  340.                ; SUBROUTINES ;
  341.                ;*************;
  342.  
  343. ;------------------------------------------------------------------------;
  344. ; This subroutine searches for a directory with a specific first letter. ;
  345. ;------------------------------------------------------------------------;
  346.  
  347. SEARCH:        CMP    BL,'a'                 ;Capitalize if lower case.
  348.                JB     SEARCH_IT
  349.                CMP    BL,'z'
  350.                JA     SEARCH_IT
  351.                AND    BL,5FH
  352. SEARCH_IT:     CMP    BL,'\'                 ;Searching for root?
  353.                JNZ    SEARCH_NAME
  354.                CALL   HOME_BAR               ;If yes, home the bar.
  355.                RET
  356.  
  357. SEARCH_NAME:   CALL   GET_NAME               ;Get current position.
  358.                XOR    DX,DX                  ;Zero out file counter.
  359.                MOV    DI,DIR_NAME            ;Store current position in DI.
  360.                MOV    SI,OFFSET DIRECTORIES  ;Point to top of listing.
  361.                CMP    BYTE PTR [DI],BL       ;Are we currently at a match?
  362.                JNZ    INC_SEARCH             ;If no, start from top.
  363. FIND_START:    INC    DX                     ;Increment count.
  364.                ADD    SI,DIR_RECORD          ;Increment record.
  365.                CMP    SI,END_OFFSET          ;End of tree?
  366.                JAE    SEARCH_BEEP            ;If yes, no match.
  367.                CMP    SI,CUR_RECORD          ;New record?
  368.                JBE    FIND_START             ;If no, find it.
  369.  
  370. NEXT_SEARCH:   XOR    BP,BP                  ;Zero in directory level pointer.
  371. FIND_DASH:     INC    BP
  372.                CMP    BYTE PTR [SI+BP-1],196 ;Is it the dash?
  373.                JNZ    FIND_DASH              ;If no, find it.
  374.  
  375.                CMP    BYTE PTR [SI+BP],BL    ;Got a match?
  376.                JZ     FOUND_IT               ;If yes, process.
  377. INC_SEARCH:    ADD    SI,DIR_RECORD          ;Else, point to next record.
  378.                INC    DX
  379.                CMP    SI,END_OFFSET          ;End of listing?
  380.                JB     NEXT_SEARCH            ;If no, keep searching.
  381. SEARCH_BEEP:   CALL   BEEP                   ;No matches, so beep.
  382.                RET
  383.  
  384. FOUND_IT:      MOV    CX,DIR_COUNT           ;Retrieve directory count.
  385.                SUB    CX,DX                  ;Subtract search count.
  386.                MOV    SEARCH_COUNT,CX        ;And store.
  387.                MOV    CL,NORMAL              ;Turn off bar for now.
  388.                MOV    BAR_ATTRIBUTE,CL
  389.                CALL   END_BAR                ;First move to end.
  390.                JMP    SHORT MARK_IT
  391. NEXT_UP:       CALL   UP_ARROW               ;Move up to matching filename.
  392. MARK_IT:       DEC    SEARCH_COUNT
  393.                JNZ    NEXT_UP
  394.                MOV    CL,INVERSE             ;Turn bar back on and display.
  395.                MOV    BAR_ATTRIBUTE,CL
  396.                XOR    BP,BP
  397.                CALL   SCROLL_BAR
  398.                RET
  399.  
  400. ;-------------------------------------------------------------------------;
  401. ; This subroutine changes the directory to the one highlighted and exits. ;
  402. ;-------------------------------------------------------------------------;
  403.  
  404. CHANGE_DIR:    CALL   GET_NAME
  405.                CALL   NEW_DIR
  406.                JMP    EXIT                   ;Exit.
  407.  
  408. ;----------------------------------------------------;
  409. ; This subroutine renames the highlighted directory. ;
  410. ;----------------------------------------------------;
  411.  
  412. RENAME:        CMP    DOS_VERSION,3          ;Can't rename directories before
  413.                JB     RENAME_ERROR           ; DOS 3.x
  414.                CALL   GET_NAME
  415.                CMP    SI,OFFSET DIRECTORIES  ;Can't rename the root directory.
  416.                JZ     RENAME_ERROR
  417.                CALL   NEW_DIR                ;Change to the requested directory.
  418.                JC     RENAME_READ            ;If nonexistant, reread data.
  419.  
  420.                MOV    DX,162CH               ;Row 22, column 44.
  421.                MOV    SI,OFFSET RENAME_MSG   ;Display rename message.
  422.                CALL   QUERY                  ;Get new name from user.
  423.                JC     RENAME_END             ;If carry, Esc was pressed.
  424.  
  425.                MOV    DX,OFFSET DOT_DOT      ;Change to parent directory.
  426.                CALL   CD
  427.                MOV    DX,DIR_NAME            ;Point to old name.
  428.                MOV    DI,80H
  429.                MOV    AH,56H                 ;Rename.
  430.                INT    21H
  431.                JC     RENAME_ERROR           ;Beep if illegal.
  432. RENAME_READ:   CALL   REREAD                 ;Reread data.
  433. RENAME_END:    CALL   CLEAR_MSG
  434.                RET
  435.  
  436. RENAME_ERROR:  CALL   BEEP                   ;Error exit.
  437.                CALL   CLEAR_MSG
  438.                RET
  439.  
  440. ;-----------------------------------------------------------------------;
  441. ; This subroutine makes a subdirectory under the highlighted directory. ;
  442. ;-----------------------------------------------------------------------;
  443.  
  444. MKDIR:         CALL   GET_NAME
  445.                CALL   NEW_DIR                ;Change to the requested directory.
  446.                JC     MKDIR_READ             ;If nonexistant, reread data.
  447.                MOV    DX,1629H               ;Row 22; column 41.
  448.                MOV    SI,OFFSET PATH_LENGTH  ;Display path message.
  449.                CALL   DISPLAY_TEXT
  450.                CALL   GET_COUNT              ;And the path length.
  451.                CALL   GET_TEXT               ;And end of message.
  452.                CMP    COUNT,63               ;Is it already 63 or more chars?
  453.                JB     MKDIR_QUERY
  454.                CALL   BEEP                   ;If yes, refuse to make directory.
  455.                CALL   DELAY
  456.                JMP    SHORT MKDIR_END
  457.  
  458. MKDIR_QUERY:   MOV    DX,1729H               ;Point to next row.
  459.                CALL   QUERY                  ;Get name from user.
  460.                JC     MKDIR_END              ;If carry, Esc was pressed.
  461.                MOV    DX,80H
  462.                MOV    AH,39H                 ;Create the directory.
  463.                INT    21H
  464.                JC     MKDIR_ERROR            ;Beep if illegal.
  465. MKDIR_READ:    CALL   REREAD                 ;Reread data.
  466.  
  467. MKDIR_END:     CALL   CLEAR_MSG
  468.                RET
  469.  
  470. MKDIR_ERROR:   CALL   BEEP                   ;Error exit.
  471.                CALL   CLEAR_MSG
  472.                RET
  473.  
  474. ;-------------------------------------------------------;
  475. ; This subroutine removes the highlighted subdirectory. ;
  476. ;-------------------------------------------------------;
  477.  
  478. RMDIR:         CALL   GET_NAME
  479.                MOV    SI,DIR_NAME
  480.                CMP    SI,OFFSET DIRECTORIES    ;Can't remove root directory.
  481.                JZ     RMDIR_ERROR
  482.                CMP    BYTE PTR [SI+DIR_RECORD+1],196   ;Can't remove directory
  483.                JZ     RMDIR_ERROR                      ; with a subdirectory.
  484.                CALL   NEW_DIR                ;Change to requested directory.
  485.                JC     RMDIR_READ             ;If nonexistant, reread data.
  486.                CALL   COUNT_THEM             ;Count and display file count.
  487.                CMP    COUNT,0                ;If empty, remove directory.
  488.                JZ     REMOVE
  489.  
  490.                MOV    DX,1729H               ;Else, display file loss Warning.
  491.                MOV    SI,OFFSET RMDIR_MSG
  492.                CALL   DISPLAY_TEXT
  493.                MOV    DX,1829H
  494.                CALL   DISPLAY_TEXT
  495.                CALL   CURSOR_ON
  496.                CALL   READ_KEY               ;Get response.
  497.                CMP    AH,15H                 ;If not "Y", exit.
  498.                JNZ    RMDIR_RETURN
  499.                CALL   WRITE_TEXT             ;Else, display it.
  500.                CALL   READ_KEY               ;Get next response.
  501.                CMP    AH,1CH                 ;If not carriage return, exit.
  502.                JNZ    RMDIR_RETURN
  503.  
  504.                CALL   FIRST_MATCH            ;Else, find all files and delete.
  505.                CALL   DELETE
  506. NEXT_DELETE:   CALL   NEXT_MATCHING
  507.                JC     REMOVE
  508.                CALL   DELETE
  509.                JMP    SHORT NEXT_DELETE
  510.  
  511. REMOVE:        MOV    DX,OFFSET DOT_DOT      ;Move to parent directory.
  512.                CALL   CD
  513.                MOV    DX,DIR_NAME
  514.                MOV    AH,3AH                 ;Remove requested directory.
  515.                INT    21H
  516.                JC     RMDIR_ERROR
  517. RMDIR_READ:    CALL   REREAD                 ;Reread data.
  518. RMDIR_RETURN:  CALL   CLEAR_MSG
  519.                RET
  520.  
  521. RMDIR_ERROR:   CALL   BEEP                   ;Error exit.
  522.                CALL   CLEAR_MSG
  523.                RET
  524.  
  525. ;------------------------------------------------------------------;
  526. ; These two subroutines hide and unhide the highlighted directory. ;
  527. ;------------------------------------------------------------------;
  528.  
  529. HIDE_UNHIDE:   CALL   GET_NAME
  530.                CMP    SI,OFFSET DIRECTORIES  ;Can't hide root directory.
  531.                JZ     HIDE_ERROR
  532.                MOV    DX,1629H               ;Row 22; column 41.
  533.                MOV    SI,OFFSET HIDE_MSG     ;Display hide message.
  534.                CALL   DISPLAY_TEXT
  535.                MOV    SI,DIR_NAME
  536.                CALL   GET_TEXT
  537.                MOV    BP,2                   ;Hidden attribute.
  538.                CALL   READ_KEY               ;Get a keystroke.
  539.                CMP    AH,23H                 ;Is it "H"?
  540.                JZ     CHANGE_IT              ;If yes, change attribute.
  541.                XOR    BP,BP                  ;Turn off hidden attribute.
  542.                CMP    AH,16H                 ;Is it "U"?
  543.                JNZ    HIDE_END               ;If no, exit.
  544.  
  545. CHANGE_IT:     CALL   NEW_DIR
  546.                JC     HIDE_READ              ;If directory nonexistant, read.
  547.                MOV    DX,OFFSET DOT_DOT      ;Change to parent directory.
  548.                CALL   CD
  549.                MOV    DX,DIR_NAME
  550.                MOV    CX,BP                  ;Retrieve requested attribute.
  551.                CALL   CHMOD                  ;And change it.
  552. HIDE_READ:     CALL   REREAD
  553.                RET
  554.  
  555. HIDE_ERROR:    CALL   BEEP
  556. HIDE_END:      CALL   CLEAR_MSG
  557.                RET
  558.  
  559. ;--------------------------------------------------------;
  560. ; This subroutine sets or resets the read-only attribute ;
  561. ; of all the files of the highlighted directory.         ;
  562. ;--------------------------------------------------------;
  563.  
  564. READ_ONLY:     CALL   GET_NAME
  565.                CALL   NEW_DIR
  566.                JC     SET_READ               ;If nonexistant directory, reread.
  567.                CALL   COUNT_THEM             ;Display file count.
  568.                CMP    COUNT,0                ;Is directory empty?
  569.                JZ     READ_END               ;If yes, nothing to mark.
  570.                MOV    DX,1729H                  ;Row 23; column 41.
  571.                MOV    SI,OFFSET READ_ONLY_MSG   ;Display read-only message.
  572.                CALL   DISPLAY_TEXT
  573.                MOV    STRIP_MASK,11111111B      ;If read-only request, nothing
  574.                MOV    ADD_MASK,  00000001B      ; to strip and add bit 1.
  575.                CALL   READ_KEY
  576.                CMP    AH,13H                    ;Is it "R"?
  577.                JZ     DO_READ                   ;If yes, turn on read-only bit.
  578.                MOV    STRIP_MASK,11111110B      ;If NOT read-only request, strip
  579.                MOV    ADD_MASK,0                ; read-only bit and add nothing.
  580.                CMP    AH,16H                    ;Is it "U"?
  581.                JNZ    READ_RETURN               ;If no, done here.
  582.  
  583. DO_READ:       CALL   DO_ATTRIB              ;Change the attribute.
  584.                CALL   CLEAR_LINE             ;Clear keystroke prompt line.
  585.                MOV    DX,1729H               ;Row 23; column 41.
  586.                MOV    SI,OFFSET HAVE_BEEN    ;Display "have been" message.
  587.                CALL   DISPLAY_TEXT
  588.                MOV    SI,OFFSET READ_MSG     ;Display rest of message.
  589.                JMP    SHORT SET_MSG
  590.  
  591. READ_RETURN:   CALL   CLEAR_MSG
  592.                RET
  593.  
  594. READ_END:      CALL   DELAY
  595.                RET
  596.  
  597. SET_READ:      CALL   REREAD
  598.                RET
  599.  
  600. ;------------------------------------------------------;
  601. ; These two subroutines set and reset the archive bit. ;
  602. ;------------------------------------------------------;
  603.  
  604. SET_RESET:     CALL   GET_NAME
  605.                CALL   NEW_DIR
  606.                JC     SET_READ               ;If nonexistant directory, reread.
  607.                CALL   COUNT_THEM             ;Display file count.
  608.                CMP    COUNT,0                ;Is directory empty?
  609.                JZ     SET_END                ;If yes, nothing to mark.
  610.                MOV    DX,1729H               ;Row 23; column 41.
  611.                MOV    SI,OFFSET ARCHIVE_MSG  ;Display archive message.
  612.                CALL   DISPLAY_TEXT
  613.                MOV    STRIP_MASK,11111111B   ;If set archive request, nothing
  614.                MOV    ADD_MASK,  00100000B   ; to strip and add bit 1.
  615.                CALL   READ_KEY
  616.                CMP    AH,1FH                 ;Is it "S"?
  617.                JZ     DO_ARCHIVE             ;If yes, turn on archive bit.
  618.                MOV    STRIP_MASK,11011111B   ;If NOT archive request, strip
  619.                MOV    ADD_MASK,0             ; archive bit and add nothing.
  620.                CMP    AH,13H                 ;Is it "R"?
  621.                JNZ    READ_RETURN            ;If no, done here.
  622.  
  623. DO_ARCHIVE:    CALL   DO_ATTRIB              ;Change the attribute.
  624.                CALL   CLEAR_LINE             ;Clear keystroke prompt line.
  625.                MOV    DX,1729H               ;Row 23; column 41.
  626.                MOV    SI,OFFSET HAVE_BEEN    ;Display "have been" message.
  627.                CALL   DISPLAY_TEXT
  628.  
  629. SET_MSG:       CMP    ADD_MASK,0             ;If add nothing, display strip
  630.                JZ     DISPLAY_MSG            ; message.
  631.                ADD    SI,4                   ;Else, display bit on message.
  632. DISPLAY_MSG:   CALL   GET_TEXT
  633. SET_END:       CALL   DELAY                  ;Wait for keystroke, then remove
  634.                RET                           ; message.
  635.  
  636. ;-----------------------------------------------;
  637. ; This subroutine displays the file count and   ;
  638. ; displacement of the highlighted subdirectory. ;
  639. ;-----------------------------------------------;
  640.  
  641. FILE_SUM:      CALL   GET_NAME
  642.                CALL   NEW_DIR                ;Change to requested directory.
  643.                JC     REREAD                 ;If nonexistant, reread data.
  644.                CALL   COUNT_THEM             ;Get file count and displacement.
  645.                CALL   DELAY                  ;Display count until keystroke.
  646.                RET
  647.  
  648. ;-------------------------------------------------------------------;
  649. ; This subroutine rereads the directory tree information from disk. ;
  650. ;-------------------------------------------------------------------;
  651.  
  652. REREAD:        MOV    PAGE_END,21*160+323    ;Reset default values.
  653.                MOV    DIR_COUNT,1
  654.                CALL   CLEAR_MSG              ;Clear the last message.
  655.                MOV    DX,162DH               ;Row 22, column 45.
  656.                MOV    SI,OFFSET LOADING      ;Display loading message.
  657.                CALL   DISPLAY_TEXT
  658.                CALL   READ_DATA              ;Read the data.
  659.                CALL   FORMAT_IT              ;Construct new tree.
  660.                CALL   START_DISPLAY          ;Display menu.
  661.                CALL   CLEAR_MSG              ;Clear sorting message.
  662.                XOR    BP,BP                  ;Update directory display.
  663.                CALL   SCROLL
  664.                MOV    SI,PAGE_END            ;Move bar if below directory
  665.                SUB    SI,160                 ; display.
  666.                CMP    SI,LINE
  667.                JA     REREAD_END
  668.                CALL   MOVE_BAR
  669. REREAD_END:    RET
  670.  
  671. ;----------------------------;
  672. ; This subroutine spawns DR. ;
  673. ;----------------------------;
  674.  
  675. DR:            CALL   GET_NAME   
  676.                CALL   NEW_DIR                ;Change the default directory.
  677.                MOV    BX,1000H               ;Shrink memory down to one segment.
  678.                MOV    AH,4AH
  679.                INT    21H
  680.  
  681.                PUSH   DS                     ;Save segment registers.
  682.                PUSH   ES
  683.                CALL   CLS                    ;Clear screen.
  684.                CLI
  685.                MOV    STACK_SEG,SS           ;Save stack segment and pointer.
  686.                MOV    STACK_PTR,SP
  687.                STI
  688.  
  689.                MOV    WORD PTR COM_LINE_PTR + 2,DS
  690.                MOV    AX,DS:[2CH]                 ;Retrieve environment segment.
  691.                MOV    ENVIRONMENT,AX              ;And put in paramter block.
  692.                MOV    DS,AX                       ;And data segment.
  693.  
  694.                XOR    AX,AX                  ;Zero out pointer.
  695. FIND_COMSPEC:  MOV    SI,AX                  ;Point to environment offset.
  696.                INC    AX                     ;Next offset.
  697.                MOV    DI,OFFSET COMSPEC      ;Find "Comspec=".
  698.                MOV    CX,8
  699.                REP    CMPSB
  700.                JNZ    FIND_COMSPEC
  701.                MOV    DX,SI                  ;What follows is Command.com path.
  702.                MOV    BX,OFFSET ENVIRONMENT  ;Point to parameter block.
  703.                MOV    AX,4B00H               ;Execute.
  704.                INT    21H
  705.  
  706.                CLI
  707.                MOV    SP,CS:STACK_PTR        ;Restore stack segment and pointer.
  708.                MOV    SS,CS:STACK_SEG
  709.                STI
  710.                POP    ES                     ;Restore segment registers.
  711.                POP    DS
  712.  
  713.                MOV    DX,80H                 ;Restore Disk Transfer Address.
  714.                MOV    AH,1AH
  715.                INT    21H
  716.                MOV    BX,2000H               ;Restore dual memory segments.
  717.                MOV    AH,4AH
  718.                INT    21H
  719.                CALL   CLS                    ;Clear screen.
  720.                CALL   START_DISPLAY          ;Restore menu.
  721.                RET
  722.  
  723. ;--------------------------------------------------------------------------;
  724. ; These six subroutines control the bar and page of the directory listing. ;
  725. ;--------------------------------------------------------------------------;
  726.  
  727. UP_ARROW:      MOV    BP,-160                ;Move bar up one line.
  728.                JMP    SHORT BAR_MOVE
  729.  
  730. DN_ARROW:      MOV    BP,160                 ;Move bar down one line.
  731. BAR_MOVE:      CALL   SCROLL_BAR
  732.                RET
  733.  
  734. PG_UP:         MOV    BP,-21*40              ;Move up 21 lines.
  735.                CALL   SCROLL
  736.                JMP    SHORT BOTTOM_BAR
  737.  
  738. PG_DN:         MOV    BP,21*40               ;Move down 21 lines.
  739. MOVE_PAGE:     CALL   SCROLL
  740.                JMP    SHORT TOP_BAR          ;Move bar to top.
  741.  
  742. HOME_BAR:      MOV    CUR_OFFSET,OFFSET DIRECTORIES ;Move listing to beginning.
  743. TOP_BAR:       MOV    SI,323                        ;And move bar to top.
  744.                CALL   MOVE_BAR
  745.                RET
  746.  
  747. END_BAR:       MOV    BX,END_OFFSET          ;Move listing to last page.
  748.                SUB    BX,21*DIR_RECORD
  749.                CMP    BX,OFFSET DIRECTORIES
  750.                JBE    BOTTOM_BAR
  751.                MOV    CUR_OFFSET,BX
  752. BOTTOM_BAR:    MOV    SI,PAGE_END            ;And move bar to bottom.
  753.                SUB    SI,160
  754.                CALL   MOVE_BAR
  755.                RET
  756.  
  757. ;----------------------------------------------;
  758. ; These subroutines support the ten functions. ;
  759. ;----------------------------------------------;
  760.  
  761. CHMOD:         MOV    AX,4301H
  762.                INT    21H
  763.                RET
  764.  
  765. ;--------------------------------------;
  766.  
  767. DELAY:         MOV    AH,1
  768.                INT    16H                    ;Loop here until keystroke.
  769.                JZ     DELAY
  770.                CALL   CLEAR_MSG
  771.                RET
  772.  
  773.  ;-------------------------------------;
  774.  
  775. DELETE:        MOV    DX,80H+30              ;Point to filename.
  776.                MOV    AH,41H
  777.                INT    21H
  778.                RET
  779.  
  780. ;--------------------------------------;
  781.  
  782. DO_ATTRIB:     CALL   FIRST_MATCH            ;Find first file.
  783. NEXT_SET:      MOV    CL,DS:[80H+21]         ;Get old attribute.
  784.                AND    CL,STRIP_MASK          ;Strip request.
  785.                OR     CL,ADD_MASK            ;Add request.
  786.                XOR    CH,CH                  ;Zero in high half.
  787.                MOV    DX,80H+30              ;Point to filename.
  788.                CALL   CHMOD                  ;And change the attribute.
  789.                CALL   NEXT_MATCHING          ;Do it to all files.
  790.                JNC    NEXT_SET
  791.                RET
  792.  
  793. ;--------------------------------------;
  794.  
  795. FIRST_MATCH:   MOV    DX,OFFSET STAR_DOT_STAR
  796.                MOV    CX,7
  797.                MOV    AH,4EH
  798.                INT    21H
  799.                RET
  800.  
  801. NEXT_MATCHING: MOV    AH,4FH
  802.                INT    21H
  803.                RET
  804.  
  805. ;------------------------------------------------------;
  806. ; This subroutine gets a directory name from the user. ;
  807. ;------------------------------------------------------;
  808.  
  809. QUERY:         CALL   DISPLAY_TEXT           ;Display caller's message.
  810.                MOV    SI,DIR_NAME            ;Add directory name.
  811.                CALL   GET_TEXT
  812.                INC    DH                     ;Next row.
  813.                CALL   SET_CURSOR             ;Move cursor.
  814.                CALL   CURSOR_ON              ;Turn it on.
  815.                MOV    DI,80H                 ;Write nulls over old entry.
  816.                XOR    AX,AX
  817.                MOV    CX,18
  818.                REP    STOSW
  819.                MOV    DI,80H                 ;Initiate pointer for entry.
  820.  
  821. GET_QUERY:     CALL   READ_KEY               ;Get a character.
  822.                CMP    AL,27                  ;Is it Esc?
  823.                JZ     QUERY_DONT             ;Is yes, exit subroutine.
  824.                CMP    AL,':'                 ;Ignore ":\?*".
  825.                JZ     GET_QUERY
  826.                CMP    AL,'\'
  827.                JZ     GET_QUERY
  828.                CMP    AL,'?'
  829.                JZ     GET_QUERY
  830.                CMP    AL,'*'
  831.                JZ     GET_QUERY
  832.                CMP    AL,13                  ;Is it carriage return?
  833.                JZ     QUERY_DO               ;If yes, return, do request.
  834.                CMP    AL,8                   ;Is it backspace?
  835.                JNZ    NOT_BS                 ;If no, skip.
  836.                CMP    DI,80H                 ;At beginning of field?
  837.                JZ     GET_QUERY              ;If yes, skip.
  838.                DEC    DI                     ;Else, decrement pointer.
  839.                MOV    SI,OFFSET BS           ;Erase last character.
  840.                CALL   GET_TEXT
  841.                JMP    SHORT GET_QUERY
  842. NOT_BS:        CMP    AL,32                  ;Is it above space?
  843.                JBE    GET_QUERY              ;If no, ignore.
  844.                CMP    DI,8CH                 ;End of entry field?
  845.                JZ     GET_QUERY              ;If yes, ignore.
  846.                STOSB                         ;Else, store byte.
  847.                CALL   WRITE_TEXT             ;And write it to screen.
  848.                JMP    SHORT GET_QUERY        ;Get next keystroke.
  849.  
  850. QUERY_DO:      MOV    BYTE PTR DS:[DI],0     ;Convert to ASCIIZ.
  851.                CLC
  852.                RET
  853.  
  854. QUERY_DONT:    STC                           ;Indicate Esc was pressed.
  855.                RET
  856.  
  857. ;--------------------------------------------;
  858. ; This subroutine gets a file count and      ;
  859. ; displacement of the highlighted directory. ;
  860. ;--------------------------------------------;
  861.  
  862. COUNT_THEM:    MOV    COUNT,0                ;Zero out file counter.
  863.                MOV    CLUST_COUNT,0          ;And cluster counter.
  864.                CALL   FIRST_MATCH            ;Get first file.
  865.                JC     DISPLAY_COUNT
  866.  
  867. FIND_NEXT:     INC    COUNT                  ;Increment file count.
  868.                MOV    AX,DS:[80H+26]         ;Retrieve low word
  869.                MOV    DX,DS:[80H+28]         ; and high word of file size.
  870.                DIV    CLUST_BYTES            ;Divide to get cluster count.
  871.                OR     DX,DX                  ;If remainder, increment count.
  872.                JZ     NO_REMAINDER
  873.                INC    CLUST_COUNT
  874. NO_REMAINDER:  ADD    CLUST_COUNT,AX         ;Add cluster count.
  875.                CALL   NEXT_MATCHING          ;Find rest of files.
  876.                JNC    FIND_NEXT
  877.  
  878. DISPLAY_COUNT: MOV    DX,1629H               ;Row 22; column 41.
  879.                CALL   SET_CURSOR
  880.                CALL   GET_COUNT              ;Display count.
  881.                MOV    SI,OFFSET FILE_COUNT   ;And message.
  882.                CALL   GET_TEXT
  883.                MOV    SI,DIR_NAME            ;Display parent directory.
  884.                CALL   GET_TEXT
  885.                MOV    SI,OFFSET USING        ;Display rest of message.
  886.                CALL   GET_TEXT
  887.                MOV    AX,CLUST_COUNT         ;Retrieve cluster count.
  888.                XOR    DX,DX                  ;Zero in high half.
  889.                MOV    BX,CLUST_BYTES
  890.                MUL    BX                     ;Multiply to get total bytes.
  891.                MOV    BX,1024
  892.                DIV    BX                     ;Divide by one K bytes.
  893.                MOV    COUNT,AX
  894.                CALL   GET_COUNT              ;Convert count to ASCII.
  895.                MOV    AL,'K'                 ;Tack on "K".
  896.                CALL   WRITE_TEXT
  897.                RET
  898.  
  899. ;------------------------------------------------------;
  900. ; This subroutine displays the loading message and     ;
  901. ; reads the disk on reads requested from command line. ;
  902. ;------------------------------------------------------;
  903.  
  904. FIRST_READ:    CALL   CLS
  905.                MOV    DX,0C18H
  906.                MOV    SI,OFFSET LOADING
  907.                CALL   DISPLAY_TEXT
  908.  
  909. ;-------------------------------------------------------------------;
  910. ; This subroutine reads the root directory and FAT from disk.       ;
  911. ; Then finds subdirectories stacking them in the database one level ;
  912. ; at a time.  Finally, the database is alphabetized by level.       ;
  913. ;-------------------------------------------------------------------;
  914.  
  915. READ_DATA:     MOV    AX,DISK_BLOCK[2]            ;Retrieve bytes per sector.
  916.                MOV    BL,BYTE PTR DISK_BLOCK[4]   ;And sectors per cluster -1.
  917.                INC    BL                          ;Adjust.
  918.                XOR    BH,BH                  ;Zero high byte.
  919.                MOV    CLUST_SECTORS,BX       ;And save.
  920.                MUL    BX                     ;Get total bytes per cluster.
  921.                MOV    CLUST_BYTES,AX         ;And save.
  922.                MOV    CX,32                  ;Divide by bytes per directory.
  923.                DIV    CX
  924.                MOV    CLUST_RECORDS,AX       ;And save.
  925.  
  926. GET_ROOT:      MOV    AX,DISK_BLOCK[9]       ;Retrieve number of root entries.
  927.                MOV    CX,32                  ;Times 32 byte record length.
  928.                MUL    CX
  929.                JNC    NO_ERROR               ;Problem if carry.
  930. DISK_EXIT:     MOV    DX,OFFSET UNKNOWN      ;Display message and exit.
  931. ERROR_EXIT:    CALL   RESTORE_DRIVE
  932. MSG_EXIT:      MOV    AH,9
  933.                INT    21H
  934.                CALL   CURSOR_ON
  935.                INT    20H
  936.  
  937. NO_ERROR:      MOV    BX,DISK_BLOCK[2]       ;Divide by bytes per sector.
  938.                DIV    BX
  939.                MOV    ROOT_SECTORS,AX        ;That's root length in sectors.
  940.                MOV    CX,AX                  ;Root length.
  941.                MOV    DX,DISK_BLOCK[16]         ;Directory starting sector.
  942.                MOV    BX,OFFSET ROOT_DIRECTORY  ;Read the root directory.
  943.                CALL   READ_DISK
  944.  
  945.                MOV    AX,DATA_SEGMENT           ;Get data storage segment.
  946.                MOV    ES,AX
  947.                XOR    BP,BP                     ;Tree level counter.
  948.                XOR    BX,BX                     ;Entry number counter.
  949.                MOV    SI,OFFSET ROOT_DIRECTORY  ;Point to root directory.
  950.                MOV    DI,OFFSET DATA_BUFFER     ;Point to database storage.
  951.                MOV    BYTE PTR ES:[DI],0FFH     ;Initialize with end signature.
  952.                MOV    ES:LEVEL_ADDRESS,DI       ;Store starting level address.
  953.                MOV    CX,DISK_BLOCK[9]          ;Retrieve no. root directories.
  954.                CALL   STORE_RECORD              ;Store them in database.
  955.                CMP    BX,0                      ;BX returns with no. entries.
  956.                JZ     DATA_END                  ;If none, done here.
  957.  
  958. GET_FAT:       PUSH   BX                          ;Save entry count.
  959.                MOV    AL,BYTE PTR DISK_BLOCK[15]  ;Retrieve sectors for FAT.
  960.                XOR    AH,AH                       ;Zero in high byte.
  961.                MOV    CX,AX                     ;Save.
  962.                MOV    BX,DISK_BLOCK[2]          ;Retrieve byte per sector.
  963.                MUL    BX                        ;Multiply to get total bytes.
  964.                JC     DISK_EXIT                 ;Problem if carry.
  965.                CMP    AX,65535-8192             ;Too big if over 56K.
  966.                JA     DISK_EXIT                 ;Display message and exit.
  967.                MOV    DX,DISK_BLOCK[6]          ;Retrieve reserved sectors.
  968.                MOV    BX,OFFSET FAT             ;Read the FAT.
  969.                CALL   READ_DISK
  970.                JC     ERROR_EXIT                ;Exit if problem.
  971.  
  972.                POP    BX                        ;Retrieve directory count.
  973.                MOV    AX,DATA_SEGMENT           ;Point to database segment.
  974.                MOV    ES,AX
  975.                MOV    SI,OFFSET DATA_BUFFER     ;And point to database offset.
  976. UP_LEVEL:      INC    BP                        ;Bump point to next level.
  977.                INC    BP
  978.                MOV    ES:[OFFSET LEVEL_ADDRESS+BP],DI   ;Save level address.
  979.                MOV    CX,BX                             ;Retrieve record count.
  980.                XOR    BX,BX                  ;Zero out entry counter.
  981. NEXT_SECTOR:   MOV    DX,ES:[SI+16]          ;Retrieve starting cluster.
  982.  
  983. NEXT_CLUSTER:  MOV    LAST_CLUSTER,DX        ;And save.
  984.                PUSH   CX                     ;Save some registers.
  985.                PUSH   BX
  986.                CALL   READ_CLUSTER           ;Retrieve directory sector.
  987.                POP    BX
  988.                MOV    DX,ES:[SI+1]           ;Retrieve parent entry number.
  989.                PUSH   SI
  990.                MOV    SI,OFFSET WORKSPACE    ;Point to new directory.
  991.                MOV    CX,CLUST_RECORDS       ;Retrieve records per cluster.
  992.                CALL   STORE_RECORD           ;Store subdirectories in database.
  993.                POP    SI
  994.                POP    CX
  995.                JC     END_LEVEL              ;If carry, last entry found.
  996.                CALL   CK_FAT                 ;Else, get next cluster.
  997.                JNC    NEXT_CLUSTER           ;If carry, last cluster found.
  998.  
  999. END_LEVEL:     ADD    SI,DATA_RECORD         ;Else, point to next root entry.
  1000.                LOOP   NEXT_SECTOR            ;Find next subdirectory.
  1001.                CMP    SI,DI                  ;Did we find any at this level?
  1002.                JNZ    UP_LEVEL               ;If yes, continue.
  1003.                MOV    AX,0FFFFH              ;Else, done; mark with signature.
  1004.                STOSB
  1005.                MOV    ES:[OFFSET LEVEL_ADDRESS+BP+2],AX   ;Mark level address.
  1006.                CALL   SORT                                ;Alphabetize by level.
  1007. DATA_END:      PUSH   CS                     ;Restore segment registers.
  1008.                PUSH   CS
  1009.                POP    DS
  1010.                POP    ES
  1011.                RET
  1012.  
  1013. ;-------------------------------------------------;
  1014. ; This subroutine does the direct sector reading. ;
  1015. ;-------------------------------------------------;
  1016.  
  1017. READ_DISK:     PUSH   BP                     ;Call destroys all registers
  1018.                PUSH   SI                     ; except segment registers
  1019.                PUSH   DI                     ; so we have to preserve.
  1020.                PUSH   DS
  1021.  
  1022.                MOV    AL,DRIVE               ;Retrieve drive.
  1023.                MOV    SI,DS                  ;Point to second segment.
  1024.                ADD    SI,1000H
  1025.                MOV    DS,SI
  1026.                INT    25H                    ;Read the sectors.
  1027.                POP    AX                     ;Get rid off flags left on stack.
  1028.  
  1029.                POP    DS                     ;Restore registers.
  1030.                POP    DI
  1031.                POP    SI
  1032.                POP    BP
  1033.                RET
  1034.  
  1035. ;----------------------------------------;
  1036. ; This subroutine will read CX clusters. ;
  1037. ;----------------------------------------;
  1038.  
  1039. READ_CLUSTER:  MOV    AX,LAST_CLUSTER        ;Retrieve cluster number.
  1040.                MOV    CX,CLUST_SECTORS       ;Retrieve sectors per cluster.
  1041.                MUL    CX                     ;Multiply to get sector start.
  1042.                MOV    DX,AX
  1043.                ADD    DX,DISK_BLOCK[11]      ;Add start of first data cluster.
  1044.                SUB    DX,CX                  ;Subtract to reflect cluster
  1045.                SUB    DX,CX                  ; 2 as first logical data sector.
  1046.                MOV    BX,OFFSET WORKSPACE    ;Point to workspace.
  1047.                CALL   READ_DISK              ;And get data.
  1048.                RET
  1049.  
  1050. ;------------------------------------------------------------------;
  1051. ; This subroutine stores the level number, entry number, parent    ;
  1052. ; entry number, the directory name and attribute byte in database. ;
  1053. ;------------------------------------------------------------------;
  1054.  
  1055. STORE_RECORD:  PUSH   DS                     ;Point to second segment.
  1056.                MOV    AX,CS
  1057.                ADD    AX,1000H
  1058.                MOV    DS,AX
  1059.  
  1060. NEXT_RECORD:   CMP    BYTE PTR [SI],0        ;No more entries?
  1061.                JZ     STORE_RETURN           ;If yes, done here.
  1062.                TEST   BYTE PTR [SI+11],10H   ;Is it a directory?
  1063.                JZ     LOOP_STORE             ;If no, next entry.
  1064.                CMP    BYTE PTR [SI],0E5H     ;Is it a removed directory?
  1065.                JZ     LOOP_STORE             ;If yes, skip.
  1066.                CMP    BYTE PTR [SI],'.'      ;Is it a dot or dot-dot entry?
  1067.                JZ     LOOP_STORE             ;If yes, skip.
  1068.  
  1069.                PUSH   SI
  1070.                MOV    AX,BP                  ;Store level number.
  1071.                STOSB
  1072.                MOV    AX,BX                  ;Store entry number.
  1073.                STOSW
  1074.                MOV    AX,DX                  ;Store parent entry number.
  1075.                STOSW
  1076.                PUSH   CX
  1077.                MOV    CX,11                  ;Store 11 bytes of name.
  1078.                REP    MOVSB
  1079.                POP    CX
  1080.                ADD    SI,15                  ;Store starting cluster.
  1081.                MOVSW
  1082.                INC    BX                     ;Increment entry count.
  1083.                POP    SI
  1084.                XOR    AX,AX                  ;Assume not a hidden directory.
  1085.                TEST   BYTE PTR [SI+11],2     ;Is it hidden?
  1086.                JZ     STORE_ATTRIB           ;If no, store null.
  1087.                MOV    AX,'H'                 ;Else, store "H".
  1088. STORE_ATTRIB:  STOSW
  1089.  
  1090. LOOP_STORE:    ADD    SI,32                  ;Point to next entry.
  1091.                LOOP   NEXT_RECORD
  1092.                POP    DS                     ;Restore segment.
  1093.                CLC                           ;Indicate full sector.
  1094.                RET
  1095.  
  1096. STORE_RETURN:  POP    DS                     ;Indicate not full sector.
  1097.                STC
  1098.                RET
  1099.  
  1100. ;-------------------------------------------------------------------;
  1101. ; This subroutine finds the next cluster in the chain from the FAT. ;
  1102. ;-------------------------------------------------------------------;
  1103.  
  1104. CK_FAT:        PUSH   ES                     ;Save some registers.
  1105.                PUSH   BX
  1106.                PUSH   CX
  1107.                MOV    AX,CS                  ;Point to second segment.
  1108.                ADD    AX,1000H
  1109.                MOV    ES,AX
  1110.  
  1111.                MOV    AX,LAST_CLUSTER        ;Retrieve last cluster.
  1112.                PUSH   AX                     ;Save.
  1113.                XOR    DX,DX                  ;Zero in high half.
  1114.                CMP    DISK_BLOCK[13],0FF6H   ;Number of clusters above 4085?
  1115.                JA     SIXTEEN_BIT            ;If yes, 16 bit FAT.
  1116. TWELVE_BIT:    MOV    BX,15                  ;Else, 12 bit FAT.
  1117.                MUL    BX                     ;Multiply by 1.5 (or 15/10).
  1118.                MOV    BX,10
  1119.                DIV    BX
  1120.                MOV    BX,AX
  1121.                MOV    DX,ES:FAT[BX]          ;Retrieve cluster pointer.
  1122.                POP    AX                     ;Retrieve last cluster.
  1123.                TEST   AX,1                   ;Was last cluster even number?
  1124.                JZ     LOW_ORDER              ;If yes, use low order 12 bits.
  1125.                MOV    CL,4                   ;Else, high order 12 bits.
  1126.                SHR    DX,CL                  ;Shift right 4 bits.
  1127.                JMP    SHORT CK_12BIT
  1128. LOW_ORDER:     AND    DX,0000111111111111B   ;Mask off top 4 bits.
  1129. CK_12BIT:      CMP    DX,0FF8H               ;Is it end of chain?
  1130.                JAE    CLUSTER_END            ;If yes, indicate so.
  1131.                JMP    SHORT RETURN_CLUST     ;Else, return with cluster in DX.
  1132.  
  1133. SIXTEEN_BIT:   POP    BX                     ;Retrieve last cluster.
  1134.                SHL    BX,1                   ;Multiply by 2.
  1135.                MOV    DX,ES:FAT[BX]          ;Retrieve next cluster number.
  1136.                CMP    DX,0FFF8H              ;Is it end of chain?
  1137.                JAE    CLUSTER_END            ;If yes, indicate so.
  1138.  
  1139. RETURN_CLUST:  POP    CX                     ;Restore registers.
  1140.                POP    BX
  1141.                POP    ES
  1142.                CLC                           ;Indicate not end of chain.
  1143.                RET
  1144.  
  1145. CLUSTER_END:   POP    CX
  1146.                POP    BX
  1147.                POP    ES
  1148.                STC                           ;Indicate end of cluster chain.
  1149.                RET
  1150.  
  1151. ;--------------------------------------------------------;
  1152. ; This subroutine alphabetizes the directories by level. ;
  1153. ;--------------------------------------------------------;
  1154.  
  1155. SORT:          PUSH   DS
  1156.                MOV    AX,DATA_SEGMENT        ;Point to database segment.
  1157.                MOV    ES,AX
  1158.                MOV    DS,AX
  1159.                XOR    BP,BP                              ;Zero in level pointer.
  1160. LEVEL_SORT:    MOV    DX,ES:[OFFSET LEVEL_ADDRESS+BP+2]  ;Retreive level offset.
  1161.                CMP    DX,0FFFFH                          ;End signature?
  1162.                JZ     SORT_RETURN            ;If yes, done here.
  1163.                SUB    DX,DATA_RECORD         ;Point to end of lower level.
  1164.                PUSH   BP                     ;Save level pointer.
  1165.  
  1166. NEXT_PASS:     POP    BP
  1167.                PUSH   BP
  1168.                MOV    BX,ES:[OFFSET LEVEL_ADDRESS+BP] ;Point to start of level.
  1169.                XOR    BP,BP                           ;Zero in exchange flag.
  1170.                CMP    BX,DX                  ;End of level?
  1171.                JZ     SORT_SET               ;If yes, next level.
  1172.  
  1173. NEXT_SORT:     MOV    SI,BX                  ;Source and destination.
  1174.                ADD    SI,5                   ;Point to name
  1175.                MOV    DI,BX                  ; in each record.
  1176.                ADD    DI,DATA_RECORD+5
  1177.                MOV    CX,11
  1178.                REPZ   CMPSB                  ;Compare names.
  1179.                JBE    END_SORT               ;If already in order, skip.
  1180.  
  1181.                MOV    SI,BX                  ;Else, recover pointers.
  1182.                MOV    DI,BX
  1183.                ADD    DI,DATA_RECORD
  1184.                MOV    CX,DATA_RECORD/2       ;Exchange the records.
  1185. NEXT_SWAP:     MOV    AX,[DI]
  1186.                MOVSW
  1187.                MOV    [SI-2],AX
  1188.                LOOP   NEXT_SWAP
  1189.                MOV    BP,1                   ;Flag that exchange was made.
  1190.  
  1191. END_SORT:      ADD    BX,DATA_RECORD         ;Point to next record.
  1192.                CMP    BX,DX                  ;End of top?
  1193.                JB     NEXT_SORT              ;If no, bubble sort next.
  1194.                SUB    DX,DATA_RECORD         ;Else, move top down one record.
  1195.                CMP    BP,0                   ;Was there exchange made?
  1196.                JNZ    NEXT_PASS
  1197.  
  1198. SORT_SET:      POP    BP
  1199.                INC    BP                     ;Next level.
  1200.                INC    BP
  1201.                JMP    SHORT LEVEL_SORT       ;Sort it.
  1202.  
  1203. SORT_RETURN:   POP    DS                     ;Restore data segment.
  1204.                RET
  1205.  
  1206. ;-------------------------------------------------------------;
  1207. ; This subroutine formats the database into a directory tree. ;
  1208. ;-------------------------------------------------------------;
  1209.  
  1210. FORMAT_IT:     PUSH   DATA_SEGMENT           ;Point to database segment.
  1211.                POP    DS
  1212.                MOV    AX,CS                  ;Point to second segment.
  1213.                ADD    AX,1000H
  1214.                MOV    ES,AX
  1215.                MOV    SI,OFFSET LEVEL_ADDRESS     ;Move resident database
  1216.                MOV    DI,SI                       ; into our second segment.
  1217.                MOV    CX,100+(DATA_RECORD*500/2)
  1218.                REP    MOVSW
  1219.  
  1220.                PUSH   CS                     ;Restore segment registers.
  1221.                PUSH   CS
  1222.                POP    DS
  1223.                POP    ES
  1224.                CALL   STORE_ROOT             ;Initialize with "\ (ROOT)".
  1225.  
  1226.                XOR    BP,BP                              ;Zero in level pointer.
  1227.                MOV    SI,OFFSET DATA_BUFFER              ;Point to database.
  1228.                MOV    DI,OFFSET DIRECTORIES+DIR_RECORD+1 ;Point to storage.
  1229.                MOV    LAST_ENTRY,DI                      ;Save current entry.
  1230.                MOV    AX,CS                  ;Point to second segment.
  1231.                ADD    AX,1000H
  1232.                MOV    DS,AX
  1233.                CMP    BYTE PTR [SI],0FFH     ;No subs signature?
  1234.                JZ     SKIP_FORMAT            ;If yes, skip format.
  1235.  
  1236. NEXT_FORMAT:   CALL   STORE_NAME             ;Format the directory name.
  1237.                ADD    SI,DATA_RECORD         ;Point to next record.
  1238.                CMP    BYTE PTR [SI],0        ;End of root directory?
  1239.                JZ     NEXT_FORMAT            ;If no, get next entry.
  1240. SKIP_FORMAT:   PUSH   CS                     ;Else, done.
  1241.                POP    DS                     ;Restore data segment.
  1242.                DEC    DI                     ;Adjust end pointer.
  1243.                MOV    END_OFFSET,DI          ;And save.
  1244.                MOV    BX,DIR_COUNT           ;Retrieve directory count.
  1245.                CMP    BX,21                  ;Enough to fill one page?
  1246.                JAE    FORMAT_END             ;If yes, use default setting.
  1247.                MOV    AX,160                 ;Calculate last record.
  1248.                MUL    BL
  1249.                ADD    AX,323                 ;Add bar offset.
  1250.                MOV    PAGE_END,AX            ;And save.
  1251. FORMAT_END:    RET
  1252.  
  1253. ;------------------------------------------------------;
  1254. ; This subroutine initializes the directory tree image ;
  1255. ; with nulls and makes the first record "\ (ROOT)".    ;
  1256. ;------------------------------------------------------;
  1257.  
  1258. STORE_ROOT:    MOV    AX,0
  1259.                MOV    DI,OFFSET DIRECTORIES
  1260.                MOV    CX,30000 / 2
  1261.                REP    STOSW
  1262.  
  1263.                MOV    DI,OFFSET DIRECTORIES
  1264.                MOV    SI,OFFSET ROOT
  1265.                MOV    CX,8
  1266.                REP    MOVSB
  1267.                RET
  1268.  
  1269. ;-----------------------------------------------------------;
  1270. ; This subroutine recursively builds the subdirectory tree. ;
  1271. ;-----------------------------------------------------------;
  1272.  
  1273. STORE_NAME:    PUSH   SI                     ;Save a couple registers.
  1274.                PUSH   BP
  1275.  
  1276.                PUSH   DI                     ;Save pointers.
  1277.                PUSH   SI
  1278.                PUSH   SI
  1279.                MOV    SI,CS:LAST_ENTRY       ;Retrieve last entry.
  1280.                MOV    DX,DI                  ;Retrieve current position.
  1281.                CMP    SI,DX                  ;Have stored any subdirectories?
  1282.                JZ     STORE_DIR                ;If no, skip tree structuring.
  1283.                MOV    BYTE PTR CS:[SI+BP],195  ;Store connecting T character.
  1284. PLACE_BAR:     ADD    SI,DIR_RECORD            ;Move to next record.
  1285.                CMP    SI,DX                    ;Are we at the current record?
  1286.                JZ     STORE_DIR                ;If yes, done here.
  1287.                MOV    BYTE PTR CS:[SI+BP],179  ;Else, store vertical line char.
  1288.                JMP    SHORT PLACE_BAR          ;Repeat until up to date.
  1289.  
  1290. STORE_DIR:     MOV    CS:LAST_ENTRY,SI       ;Store current position.
  1291.                POP    SI                     ;Restore current record pointer.
  1292.                INC    CS:DIR_COUNT           ;Increment directory counter.
  1293.                ADD    DI,BP                  ;Add level pointer.
  1294.                MOV    AL,192                 ;Store L line character.
  1295.                STOSB
  1296.                MOV    AL,196                 ;Store horizontal line character.
  1297.                STOSB
  1298.                ADD    SI,5                   ;Point to directory name.
  1299.                MOV    CX,8
  1300.                CALL   STORE_BYTES            ;And store in tree.
  1301.                CMP    BYTE PTR [SI],32
  1302.                JZ     END_NAME
  1303.                MOV    AL,'.'                 ;If exists, append extension.
  1304.                STOSB
  1305.                MOV    CX,3
  1306.                CALL   STORE_BYTES
  1307. END_NAME:      POP    SI                     ;Restore current record pointer.
  1308.                INC    DI                     ;Bump pointer for space.
  1309.                MOV    AL,[SI+18]             ;Retrieve "hidden" field.
  1310.                STOSB                         ;And store it.
  1311. NEXT_DEST:     POP    DI                     ;Restore destination pointer.
  1312.                ADD    DI,DIR_RECORD          ;Point to next tree record.
  1313.  
  1314.                PUSH   CS:LAST_ENTRY          ;Save current position pointer.
  1315.                MOV    CS:LAST_ENTRY,DI       ;Save our current position.
  1316.                INC    BP                     ;Bump pointer to next level.
  1317.                INC    BP
  1318.                MOV    BX,DS:[OFFSET LEVEL_ADDRESS+BP] ;Get level offset.
  1319.  
  1320. NEXT_SUB:      MOV    AL,DS:[BX]             ;Retrieve level pointer.
  1321.                XOR    AH,AH
  1322.                CMP    AX,BP                  ;Is it our level?
  1323.                JA     STORE_END              ;If next level, done here.
  1324.                MOV    AX,DS:[BX+3]           ;Retrieve parent entry number.
  1325.                CMP    AX,DS:[SI+1]           ;Is it our entry number?
  1326.                JNZ    SKIP_SUB               ;If no, check next record.
  1327.  
  1328. GET_SUB:       PUSH   BX                     ;Else, save pointers.
  1329.                PUSH   SI
  1330.                MOV    SI,BX                  ;Point to our offset.
  1331.                CALL   STORE_NAME             ;Store subdirectory.
  1332.                POP    SI                     ;Restore pointers.
  1333.                POP    BX
  1334.  
  1335. SKIP_SUB:      ADD    BX,DATA_RECORD         ;Next record.
  1336.                JMP    SHORT NEXT_SUB
  1337.  
  1338. STORE_END:     POP    CS:LAST_ENTRY          ;Restore last entry pointer.
  1339.                POP    BP
  1340.                POP    SI
  1341.                RET
  1342.  
  1343. ;--------------------------------------;
  1344.  
  1345. STORE_BYTES:   LODSB                         ;Store all non space name
  1346.                CMP    AL,32                  ; characters.
  1347.                JZ     SKIP_STORE
  1348.                STOSB
  1349. SKIP_STORE:    LOOP   STORE_BYTES
  1350.                RET
  1351.  
  1352. ;-----------------------------------------------------------------;
  1353. ; This subdirectory works its way back to the root directory from ;
  1354. ; the currently highlighted directory and then retraces back up   ;
  1355. ; the tree changing the default directory one level at a time.    ;
  1356. ;-----------------------------------------------------------------;
  1357.  
  1358. NEW_DIR:       MOV    COUNT,0                ;Zero out path counter.
  1359.                MOV    DX,OFFSET ROOT_DIR     ;Change to root directory.
  1360.                CALL   CD
  1361.                XOR    CX,CX                  ;Zero in level counter.
  1362.                MOV    DI,CUR_RECORD          ;Point to highlighted record.
  1363.                CMP    DI,OFFSET DIRECTORIES  ;Is it the root?
  1364.                JZ     NEW_DIR_END            ;If yes, done here.
  1365.                MOV    BX,DIR_NAME            ;Point to directory name.
  1366.                DEC    BX                     ;Point to horizontal character.
  1367.  
  1368. NEXT_SUBDIR:   MOV    SI,BX
  1369. FIND_DIR:      LODSB                         ;Get a byte.
  1370.                CMP    AL,196                 ;Is it horizonatal character?
  1371.                JNZ    FIND_DIR               ;If no, find it.
  1372.                PUSH   SI                     ;Save directory.
  1373.                INC    CX                     ;Also keep track of level.
  1374. NEXT_PATH:     INC    COUNT                  ;Increment path length.
  1375.                LODSB
  1376.                CMP    AL,0                   ;Are we at end of directory name?
  1377.                JNZ    NEXT_PATH              ;If no, find it.
  1378.                CMP    BYTE PTR [DI+2],196    ;Is there a level under us?
  1379.                JZ     CHANGE_THEM            ;If no, done here.
  1380.                DEC    BX                     ;Else, decrement level pointer.
  1381.                DEC    BX
  1382. NEXT_LEVEL:    SUB    DI,DIR_RECORD          ;Look for next lower level.
  1383.                SUB    BX,DIR_RECORD
  1384.                CMP    BYTE PTR [BX],196      ;Have we found parent record?
  1385.                JNZ    NEXT_LEVEL             ;If no, find it.
  1386.                JMP    SHORT NEXT_SUBDIR      ;And find name and length.
  1387.  
  1388. CHANGE_THEM:   POP    DX                     ;Retrieve directory name pointer.
  1389.                CALL   CD                     ;Change directories.
  1390.                JC     RESTORE_STACK          ;If nonexistant, restore stack
  1391.                LOOP   CHANGE_THEM            ;Else, continue up the tree.
  1392. NEW_DIR_END:   RET
  1393.  
  1394. NEXT_STACK:    POP    DX                     ;If error, restore stack
  1395. RESTORE_STACK: LOOP   NEXT_STACK             ; before returning.
  1396.                RET
  1397.  
  1398. ;--------------------------------------;
  1399.  
  1400. CD:            MOV    AH,3BH
  1401.                INT    21H
  1402.                RET
  1403.  
  1404. ;--------------------------------------------------;
  1405. ; This subroutine finds the highlighted directory. ;
  1406. ;--------------------------------------------------;
  1407.  
  1408. GET_NAME:      MOV    SI,CUR_OFFSET          ;Get top of page.
  1409.                MOV    AX,LINE                ;Get location of bar.
  1410.                SUB    AX,323                 ;Adjust.
  1411.                MOV    CL,160                 ;80 characters + 80 attributes.
  1412.                DIV    CL                     ;Divide to get line number.
  1413.                MOV    CL,DIR_RECORD          ;Times directory record length.
  1414.                MUL    CL
  1415.                ADD    SI,AX                  ;Add to current offset.
  1416.                MOV    CUR_RECORD,SI                    ;And save pointer.
  1417.                CMP    CUR_RECORD,OFFSET DIRECTORIES    ;Is it top line?
  1418.                JZ     NAME_RETURN                      ;If yes, done here.
  1419. FIND_TOP:      LODSB
  1420.                CMP    AL,196                 ;Is it the horizontal line char?
  1421.                JNZ    FIND_TOP               ;If no, find it.
  1422. NAME_RETURN:   MOV    DIR_NAME,SI            ;And store name pointer.
  1423.                RET
  1424.  
  1425. ;----------------------------------------------------------;
  1426. ; This subdirectory searches through the graphic tree for  ;
  1427. ; the name of the DOS default directory and highlights it. ;
  1428. ;----------------------------------------------------------;
  1429.  
  1430. GET_CURRENT:   MOV    SI,OFFSET CURRENT_DIR  ;Point to default directory.
  1431.                MOV    BYTE PTR [SI],'\'      ;DOS fails to include starting "\".
  1432.                INC    SI                     ;Point to first level.
  1433.                MOV    DL,DRIVE               ;Retrieve drive.
  1434.                INC    DL                     ;Convert to DOS format.
  1435.                MOV    AH,47H                 ;Retrieve current directory.
  1436.                INT    21H
  1437.                CALL   CAPITALIZE             ;Capitalize it.
  1438.  
  1439.                MOV    BX,OFFSET CURRENT_DIR+1     ;Point to default directory.
  1440.                CMP    BYTE PTR [BX],0             ;Are we at end?
  1441.                JZ     CURRENT_END                           ;If yes, done here.
  1442.                MOV    BP,OFFSET DIRECTORIES+DIR_RECORD+3    ;Store pointer.
  1443.                XOR    DX,DX                                 ;Zero entry counter.
  1444.                MOV    CX,500                 ;Search a maximum of 500 records.
  1445.  
  1446. NEXT_DEFAULT:  INC    DX                     ;Increment entry counter.
  1447.                MOV    SI,BX                  ;Point to default directory.
  1448.                MOV    DI,BP                  ;Point to directory tree.
  1449.                CMP    BYTE PTR [DI-1],196    ;Is it horizontal bar?
  1450.                JNZ    NO_MATCH               ;If no, next record.
  1451. NEXT_MATCH:    CMP    BYTE PTR [DI],0        ;Is it end of tree name?
  1452.                JNZ    CK_DEFAULT             ;If no, see if name matches.
  1453.                CMP    BYTE PTR [SI],'\'      ;Is it end of default level?
  1454.                JZ     MATCH                  ;If yes, then we have a match.
  1455.                CMP    BYTE PTR [SI],0        ;Zero also marks name end.
  1456.                JZ     MATCH
  1457. CK_DEFAULT:    CMPSB                         ;Character in name match?
  1458.                JZ     NEXT_MATCH             ;If yes, check next byte.
  1459. NO_MATCH:      ADD    BP,DIR_RECORD          ;Else, next record.
  1460.                LOOP   NEXT_DEFAULT           ;Search up to 500 records.
  1461.                JMP    SHORT CURRENT_END      ;Give up if not found.
  1462.  
  1463. MATCH:         DEC    CX                     ;Decrement search counter.
  1464.                JZ     CURRENT_END            ;Exit, if not found.
  1465.                INC    SI                     ;Point to name.
  1466.                MOV    BX,SI                  ;Store current level.
  1467.                ADD    BP,DIR_RECORD+2        ;Point to next record and level.
  1468.                CMP    BYTE PTR [SI-1],0      ;End of default?
  1469.                JNZ    NEXT_DEFAULT           ;If no, continue.
  1470.  
  1471. FIND_DEFAULT:  MOV    CX,DIR_COUNT           ;Retrieve directory count.
  1472.                SUB    CX,DX                  ;Subtract default entry counter.
  1473.                MOV    SEARCH_COUNT,CX        ;Save.
  1474.                MOV    CL,NORMAL              ;Turn off bar for now.
  1475.                MOV    BAR_ATTRIBUTE,CL
  1476.                CALL   END_BAR                ;First move to end.
  1477.                JMP    SHORT FIND_IT
  1478. NEXT_FIND:     CALL   UP_ARROW               ;Move up to matching directory.
  1479. FIND_IT:       DEC    SEARCH_COUNT
  1480.                JNZ    NEXT_FIND
  1481.                MOV    CL,INVERSE             ;Turn bar back on.
  1482.                MOV    BAR_ATTRIBUTE,CL
  1483. CURRENT_END:   RET
  1484.  
  1485. ;-----------------------------------------------------;
  1486. ; This subroutine displays the menu, requested drive, ;
  1487. ; and directory count.  Also displays highlight bar.  ;
  1488. ;-----------------------------------------------------;
  1489.  
  1490. START_DISPLAY: MOV    SI,OFFSET MENU         ;Point to menu position.
  1491.                MOV    DI,(2*160)+98          ;And to screen position.
  1492.                MOV    BH,19                  ;Display 19 lines of menu.
  1493. NEXT_FUNCTION: MOV    CX,22                  ;22 characters per line.
  1494. NEXT_MENU:     LODSB
  1495.                MOV    BL,AL
  1496.                CALL   WRITE_SCREEN
  1497.                LOOP   NEXT_MENU
  1498.                ADD    DI,116                 ;Next line.
  1499.                DEC    BH
  1500.                JNZ    NEXT_FUNCTION
  1501.  
  1502.                MOV    SI,OFFSET HEADING      ;Display "Directories of drive".
  1503.                MOV    DX,1
  1504.                CALL   DISPLAY_TEXT
  1505.                MOV    AL,DRIVE
  1506.                ADD    AL,'A'                 ;Display drive.
  1507.                CALL   WRITE_TEXT
  1508.                MOV    AL,':'                 ;And colon.
  1509.                CALL   WRITE_TEXT
  1510.                CALL   COUNT_DIRS             ;Display directory count.
  1511.                MOV    SI,LINE
  1512.                CALL   MOVE_BAR               ;Highlight the current directory.
  1513.                RET
  1514.  
  1515. ;----------------------------------------------------;
  1516. ; This subroutine capitalizes the default directory. ;
  1517. ;----------------------------------------------------;
  1518.  
  1519. CAPITALIZE:    CMP    BYTE PTR [SI],'a'
  1520.                JB     NEXT_CAPS
  1521.                CMP    BYTE PTR [SI],'z'
  1522.                JA     NEXT_CAPS
  1523.                AND    BYTE PTR [SI],5FH
  1524. NEXT_CAPS:     INC    SI
  1525.                CMP    BYTE PTR [SI],0
  1526.                JNZ    CAPITALIZE
  1527.                RET
  1528.  
  1529. ;-------------------------------------;
  1530. ; This subroutine scrolls the screen. ;
  1531. ;-------------------------------------;
  1532.  
  1533. SCROLL:        MOV    SI,CUR_OFFSET          ;Get current offset.
  1534.                ADD    SI,BP                  ;Add requested direction.
  1535. CK_LOWER:      CMP    SI,OFFSET DIRECTORIES  ;If above start check upper limit.
  1536.                JAE    UPPER_LIMIT
  1537. LOWER_LIMIT:   MOV    CUR_OFFSET,OFFSET DIRECTORIES   ;Else, make it start.
  1538.                JMP    SHORT SCROLL_RETURN             ;And update screen.
  1539.  
  1540. UPPER_LIMIT:   MOV    BX,END_OFFSET                        ;See if beyond end of
  1541.                CMP    BX,OFFSET DIRECTORIES+21*DIR_RECORD  ; directory listing.
  1542.                JA     CK_UPPER
  1543.                MOV    CUR_OFFSET,OFFSET DIRECTORIES
  1544.                JMP    SHORT SCROLL_RETURN
  1545.  
  1546. CK_UPPER:      SUB    BX,21*DIR_RECORD
  1547.                CMP    SI,BX
  1548.                JBE    END_SCROLL
  1549.                MOV    SI,BX
  1550.  
  1551. END_SCROLL:    MOV    CUR_OFFSET,SI          ;Update current offset.
  1552.                MOV    SI,LINE
  1553.                CALL   MOVE_BAR
  1554. SCROLL_RETURN: RET
  1555.  
  1556. ;--------------------------------------------------;
  1557. ; This subroutine scrolls the bar if between start ;
  1558. ; and end of page. Otherwise the page is scrolled. ;
  1559. ;--------------------------------------------------;
  1560.  
  1561. SCROLL_BAR:    MOV    SI,LINE                ;Get current line.
  1562.                ADD    SI,BP                  ;Add requested line.
  1563.                MOV    BP,-DIR_RECORD         ;Assume below beginning.
  1564.                CMP    SI,323                 ;Is it?
  1565.                JB     SCROLL_PAGE            ;If yes, scroll page instead.
  1566.                MOV    BP,DIR_RECORD          ;Do the same for end of page.
  1567.                CMP    SI,PAGE_END
  1568.                JAE    SCROLL_PAGE
  1569.                CALL   MOVE_BAR
  1570.                RET
  1571.  
  1572. SCROLL_PAGE:   CALL   SCROLL
  1573.                RET
  1574.  
  1575. ;----------------------------------------------------;
  1576. ; This subroutine does the actual moving of the bar. ;
  1577. ;----------------------------------------------------;
  1578.  
  1579. MOVE_BAR:      MOV    DI,BAR_START           ;Remove old bar.
  1580.                MOV    CX,BAR_LENGTH
  1581.                MOV    BL,NORMAL
  1582. NEXT_BAR:      CALL   WRITE_SCREEN
  1583.                LOOP   NEXT_BAR
  1584.  
  1585.                MOV    LINE,SI                ;And move bar to new line.
  1586.                MOV    BL,BAR_ATTRIBUTE
  1587.                CALL   GET_NAME
  1588.                MOV    SI,CUR_RECORD          ;Retrieve current record.
  1589.                MOV    DI,LINE                ;Retrieve line.
  1590.                MOV    BAR_LENGTH,0           ;Zero out bar length counter.
  1591. START_BAR:     LODSB
  1592.                CMP    AL,'\'                 ;Is it the root?
  1593.                JZ     SAVE_START             ;If yes, done here.
  1594.                INC    DI                     ;Increment bar pointer
  1595.                INC    DI                     ; past character as well.
  1596.                CMP    AL,196                 ;Is it horizontal bar character?
  1597.                JNZ    START_BAR              ;If no, find it.
  1598.                INC    SI                     ;Bump point to start of name.
  1599.  
  1600. SAVE_START:    MOV    BAR_START,DI           ;Save.
  1601. DISPLAY_BAR:   INC    BAR_LENGTH             ;Increment bar length counter.
  1602.                MOV    CX,1
  1603.                CALL   WRITE_SCREEN           ;Write the attribute.
  1604.                LODSB
  1605.                CMP    AL,0                   ;Are we at end of name?
  1606.                JNZ    DISPLAY_BAR            ;Continue until done.
  1607.                RET
  1608.  
  1609. ;-------------------------------------------------;
  1610. ; This subroutine displays the directory listing. ;
  1611. ;-------------------------------------------------;
  1612.  
  1613. UPDATE_SCREEN: XOR    BP,BP
  1614.                MOV    SI,CUR_OFFSET          ;Retrieve starting offset.
  1615.                MOV    DI,2*160+2             ;Point to row two of screen.
  1616.                MOV    BH,21                  ;21 lines to write.
  1617. NEXT_WRITE:    MOV    CX,40                  ;40 characters per line.
  1618. NEXT_CHAR:     LODSB                         ;Get a byte.
  1619.                MOV    BL,AL                  ;Save it in BL.
  1620.                CALL   WRITE_SCREEN           ;Write them.
  1621.                LOOP   NEXT_CHAR
  1622.                ADD    DI,80                  ;Bump pointer to next line.
  1623.                DEC    BH                     ;Do all 21 lines.
  1624.                JNZ    NEXT_WRITE
  1625.                RET
  1626.  
  1627. ;------------------------------------------------------------;
  1628. ; This subroutine displays the directory by writing directly ;
  1629. ; to the screen buffer. To avoid screen noise (snow) on the  ;
  1630. ; color card, the horizontal retrace has to be monitored.    ;
  1631. ;------------------------------------------------------------;
  1632.  
  1633. WRITE_SCREEN:  MOV    DX,STATUS_REG          ;Retrieve status register.
  1634.                MOV    AX,VIDEO_SEG           ;Point to screen segment.
  1635.                MOV    ES,AX
  1636.  
  1637. HORZ_RET:      IN     AL,DX                  ;Get status.
  1638.                TEST   AL,1                   ;Is it low?
  1639.                JNZ    HORZ_RET               ;If not, wait until it is.
  1640.                CLI                           ;No more interrupts.
  1641.  
  1642. WAIT:          IN     AL,DX                  ;Get status.
  1643.                TEST   AL,1                   ;Is it high?
  1644.                JZ     WAIT                   ;If no, wait until it is.
  1645.                MOV    AL,BL                  ;Retrieve character; now it's OK
  1646.                STOSB                         ; to write to screen buffer.
  1647.                STI                           ;Interrupts back on.
  1648.                INC    DI                     ;Bump pointer past attribute.
  1649.                PUSH   CS
  1650.                POP    ES
  1651.                RET                           ;Return
  1652.  
  1653. ;-----------------------------------------------------------------------;
  1654. ; These two subroutines clear either the messages or the entire screen. ;
  1655. ;-----------------------------------------------------------------------;
  1656.  
  1657. CLEAR_MSG:     CALL   CURSOR_OFF
  1658.                MOV    CX,1629H               ;Row 22; column 41.
  1659.                MOV    DX,184FH               ;Row 24; column 79.
  1660.                JMP    SHORT CLEAR_WINDOW
  1661.  
  1662. CLEAR_LINE:    MOV    CX,1729H               ;Row 23; column 41.
  1663.                MOV    DX,174FH               ;Row 23; column 79.
  1664.                JMP    SHORT CLEAR_WINDOW
  1665.  
  1666. CLS:           XOR    CX,CX
  1667.                MOV    DX,184FH               ;Entire screen.
  1668.  
  1669. CLEAR_WINDOW:  PUSH   BP
  1670.                PUSH   BX
  1671.                MOV    BH,NORMAL              ;Clear with original attribute.
  1672.                MOV    AX,600H
  1673.                INT    10H
  1674.                POP    BX
  1675.                POP    BP
  1676.                RET
  1677.  
  1678. ;-----------------------------------------;
  1679. ; These subroutines display the messages. ;
  1680. ;-----------------------------------------;
  1681.  
  1682. DISPLAY_TEXT:  CALL   SET_CURSOR             ;Move cursor.
  1683. GET_TEXT:      LODSB
  1684.                CMP    AL,0                   ;Zero marks end of string.
  1685.                JZ     END_TEXT
  1686.                CALL   WRITE_TEXT
  1687.                JMP    SHORT GET_TEXT
  1688. END_TEXT:      RET
  1689.  
  1690. WRITE_TEXT:    PUSH   SI                     ;BIOS does not save SI.
  1691.                MOV    AH,0EH                 ;Write teletype.
  1692.                INT    10H
  1693.                POP    SI
  1694.                RET
  1695.  
  1696. ;---------------------------------------------------------;
  1697. ; These four subroutines move the cursor, get the current ;
  1698. ; directory, beep the speaker or get a keystroke.         ;
  1699. ;---------------------------------------------------------;
  1700.  
  1701. SET_CURSOR:    PUSH   SI
  1702.                XOR    BH,BH                  ;Page zero.
  1703.                MOV    AH,2                   ;Set cursor.
  1704.                INT    10H
  1705.                POP    SI
  1706.                RET
  1707.  
  1708. BEEP:          MOV    DL,7                   ;Beep via DOS.
  1709.                MOV    AH,2
  1710.                INT    21H
  1711.                RET
  1712.  
  1713. READ_KEY:      MOV    AH,0                   ;Retrieve keystroke via BIOS.
  1714.                INT    16H
  1715.                RET
  1716.  
  1717. ;-----------------------------------------------;
  1718. ; These subroutines turn the cursor off and on. ;
  1719. ;-----------------------------------------------;
  1720.  
  1721. CURSOR_OFF:    MOV    CX,2000H
  1722.                JMP    SHORT SET_TYPE
  1723.  
  1724. CURSOR_ON:     MOV    CX,CURSOR_TYPE
  1725.  
  1726. SET_TYPE:      MOV    AH,1
  1727.                INT    10H
  1728.                RET
  1729.  
  1730. ;----------------------------------------------;
  1731. ; This subroutine displays the count of files. ;
  1732. ;----------------------------------------------;
  1733.  
  1734. COUNT_DIRS:    MOV    DX,1802H               ;Row 24; column 2.
  1735.                CALL   SET_CURSOR
  1736.                MOV    AX,DIR_COUNT
  1737.                CALL   GET_DIR_COUNT
  1738.                MOV    SI,OFFSET DIR_MSG      ;Display directory count.
  1739.                CALL   GET_TEXT
  1740.                RET
  1741.  
  1742. ;------------------------------------------;
  1743. ; This subroutine converts hex to decimal. ;
  1744. ;------------------------------------------;
  1745.  
  1746. GET_COUNT:     MOV    AX,COUNT
  1747. GET_DIR_COUNT: MOV    BX,10                  ;Convert to decimal.
  1748.                XOR    CX,CX                  ;Zero in counter.
  1749. NEXT_COUNT:    XOR    DX,DX
  1750.                DIV    BX
  1751.                ADD    DL,'0'                 ;Convert to ASCII.
  1752.                PUSH   DX                     ;Save results.
  1753.                INC    CX                     ;Also increment count.
  1754.                CMP    AX,0                   ;Are we done?
  1755.                JNZ    NEXT_COUNT
  1756.  
  1757. NEXT_NUMBER:   POP    AX                     ;Retrieve numbers.
  1758.                CALL   WRITE_TEXT             ;And write them.
  1759.                LOOP   NEXT_NUMBER
  1760.                RET
  1761.  
  1762. ;---------------------------------------------;
  1763. ; This subroutine restores the current drive. ;
  1764. ;---------------------------------------------;
  1765.  
  1766. RESTORE_DRIVE: PUSH   DX
  1767.                MOV    DL,DEFAULT_DRIVE
  1768.                MOV    AH,0EH
  1769.                INT    21H
  1770.                POP    DX
  1771.                RET
  1772.  
  1773. ;------------------------------------;
  1774.  
  1775. DISK_BLOCK     LABEL  WORD
  1776. CURRENT_DIR    EQU    DISK_BLOCK+18
  1777. LEVEL_ADDRESS  EQU    CURRENT_DIR+65
  1778. DIRECTORIES    EQU    LEVEL_ADDRESS+100
  1779. DATA_BUFFER    EQU    LEVEL_ADDRESS+100
  1780. END_RESIDENT   EQU    DATA_BUFFER+(DATA_RECORD*500)
  1781.  
  1782. CODE ENDS
  1783. END  START
  1784.